<?php
/** 
 * 新后台基类
 *
 * @author   xionggan
 * @date     2019-09-11
 */

namespace app\base\controller;

use think\Config;
use think\Hook;
use think\Lang;
use think\Loader;
use think\Session;
use think\Request;
use think\Response;
use think\Validate;
use think\Controller;
use think\exception\HttpResponseException;
use app\base\model\AuthRuleModel;
use service\UserTokenService;

/**
 * 后台控制器基类
 */
class Backend extends Controller
{

    /**
     * @var 用户id
     */
    protected $user_id = '';

    /**
     * @var 用户身份id
     */
    protected $identity_id = '';

    /**
     * @var 令牌
     */
    protected $token = '';

    /**
     * @var 令牌来路
     */
    protected $token_from = '1';

    /**
     * @var Request Request 实例
     */
    protected $request;

    /**
     * 无需登录的方法,同时也就不需要鉴权了
     * @var array
     */
    protected $noNeedLogin = [];

    /**
     * 无需鉴权的方法,但需要登录
     * @var array
     */
    protected $noNeedRight = [];

    /**
     * 默认响应输出类型,支持json/xml
     * @var string
     */
    protected $responseType = 'json';

    /**
     * 当前接口路径
     * @var array
     */
    protected $path = '';

    /**
     * 当前权限规则
     * @var array
     */
    protected $rule = '';

    /**
     * 当前权限动作
     * @var array
     */
    protected $action = '';

    /**
     * 接口响应状态码
     */
    const CODE_ERROR    = 0;
    const CODE_SUCCESS  = 1;
    const CODE_LOGIN    = 2;

    /**
     * 构造方法
     * @param Request   $request  Request对象
     */
    public function __construct(Request $request = null)
    {
        $this->request = is_null($request) ? Request::instance() : $request;
        $this->_initialize();
    }

    /**
     * 初始化
     */
    protected function _initialize()
    {
        $modulename     = $this->request->module();
        $controllername = Loader::parseName($this->request->controller());
        $actionname     = strtolower($this->request->action());
        $this->path     = $modulename . '/' . str_replace('.', '/', $controllername) . '/' . $actionname;
        
        if (!in_array($this->path, $this->noNeedLogin)) {
            $this->auth();
        }

        if (!in_array($this->path, $this->noNeedRight)) {
            $this->right();
        }
    }

    /**
     * 设置token
     * @param string    $token  令牌
     */
    protected function setToken($token)
    {
        $this->token = $token;
    }

    /**
     * 操作成功返回的数据
     * @param string    $msg        提示信息
     * @param mixed     $data       要返回的数据
     * @param int       $code       错误码，默认为1
     * @param string    $type       输出类型
     * @param array     $header     发送的 Header 信息
     */
    protected function success($msg='', $data='', $code=self::CODE_SUCCESS, $type='', array $header=[])
    {
        $this->result($msg, $data, $code, $type, $header);
    }

    /**
     * 操作失败返回的数据
     * @param string    $msg        提示信息
     * @param mixed     $data       要返回的数据
     * @param int       $code       错误码，默认为0
     * @param string    $type       输出类型
     * @param array     $header     发送的 Header 信息
     */
    protected function error($msg='', $data='', $code=self::CODE_ERROR, $type='', array $header=[])
    {
        $this->result($msg, $data, $code, $type, $header);
    }

    /**
     * 返回封装后的 API 数据到客户端
     * @param mixed     $msg        提示信息
     * @param mixed     $data       要返回的数据
     * @param int       $code       错误码，默认为0
     * @param string    $type       输出类型，支持json/xml/jsonp
     * @param array     $header     发送的 Header 信息
     * @return void
     */
    protected function result($msg, $data='', $code=self::CODE_ERROR, $type='', array $header=[])
    {
        $result = [
            'code'  => $code,
            'msg'   => $msg,
            'time'  => date('Y-m-d H:i:s', $this->request->server('REQUEST_TIME')),
            'data'  => $data,
            'token' => $this->token,
        ];
        
        $type = $type ? $type : $this->responseType;

        if (isset($header['statuscode'])) {
            $code = $header['statuscode'];
            unset($header['statuscode']);
        } else {
            $code = $code >= 1000 || $code < 200 ? 200 : $code;
        }

        if (!empty($this->token)) {
            $header['Token'] = $this->token;
        }

        if (!empty($this->action)) {
            $result['action'] = $this->action;
        }

        $response = Response::create($result, $type, $code)->header($header);
        throw new HttpResponseException($response);
    }

    /**
     * 接口参数校验
     * @param array     $rule       校验规则
     * @param array     $msg        校验错误提示
     * @param array     $params     被校验的参数
     */
    protected function paramCheck($rule, $msg, $params)
    {
        $data       = [];
        $validate   = new Validate($rule, $msg);
        $result     = $validate->check($params);

        if (!$result) {
            return $validate->getError();
        }

        foreach ($rule as $key => $value) {
            $data[$key] = $params[$key] ?? '';
        }

        return $data;
    }

    /**
     * 身份验证
     */
    private function auth()
    {
        if ($token = $this->request->header('token')) {
            $this->token_from = 1;
        } elseif ($token = $this->request->param('token')) {
            $this->token_from = 2;
        }

        if (empty($token)) {
            $this->error('请先登录', '', self::CODE_LOGIN);
        }

        $token_service  = new UserTokenService();
        $token_decode   = $token_service->checkToken($token);

        if (!empty($token_service->getError())) {
            $this->error($token_service->getError(), '', self::CODE_LOGIN);
        }

        $this->token        = $token_service->flushToken($token);
        $this->user_id      = $token_decode['uid'];
        $this->identity_id  = $token_decode['ide'];
    }

    /**
     * 接口鉴权
     */
    private function right()
    {
        $rule_model = new AuthRuleModel();
        $rule_data  = $rule_model->getOneByPath($this->path);

        if (empty($rule_data)) {
            $this->error('无权限访问', '', self::CODE_LOGIN);
        }

        $this->rule = $rule_data;

        //todo 检测用户权限 !!!

        $rule_child = $rule_model->getChildByPk($rule_data['rule_id']);

        if (empty($rule_child)) {
            return;
        }

        foreach ($rule_child as $value) {
            $tag = explode('/', $value['path']);
            $action[] = [
                'rule_id'   => $value['rule_id'],
                'tag'       => end($tag),
                'path'      => $value['path'],
                'route'     => $value['route'],
                'title'     => $value['title'],
                'icon'      => $value['icon'],
                'display'   => 1,
            ];
        }

        $this->action = $action;     
    }

}
